package cloud.keveri.registry.nacos;

import cloud.keveri.base.KeveriException;
import cloud.keveri.gateway.dto.ServiceDefinition;
import cloud.keveri.gateway.dto.ServiceInstance;
import cloud.keveri.registry.api.GatewayServiceListener;
import cloud.keveri.registry.api.InstanceRegisterRepository;
import cloud.keveri.gateway.dto.KeveriClientConfigure;
import cloud.keveri.spi.Join;
import com.alibaba.fastjson2.JSON;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingFactory;
import com.alibaba.nacos.api.naming.NamingMaintainFactory;
import com.alibaba.nacos.api.naming.NamingMaintainService;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.listener.Event;
import com.alibaba.nacos.api.naming.listener.EventListener;
import com.alibaba.nacos.api.naming.listener.NamingEvent;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.Service;
import com.alibaba.nacos.api.naming.pojo.ServiceInfo;
import com.alibaba.nacos.common.executor.NameThreadFactory;
import com.alibaba.nacos.common.utils.CollectionUtils;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static cloud.keveri.registry.nacos.RegistryNacosErrorCodeConstants.NACOS_INIT_ERROR;
import static cloud.keveri.registry.nacos.RegistryNacosErrorCodeConstants.NACOS_REGISTRY_ERROR;
import static cloud.keveri.registry.nacos.def.Const.*;

/**
 * <p>
 * 作者：沈自在
 **/
@Slf4j
@Join
public class NacosRegistryRepository implements InstanceRegisterRepository {

    /**
     *
     */
    private String env;
    /**
     *
     */
    private NamingService namingService;

    /**
     *
     */
    private NamingMaintainService namingMaintainService;

    /**
     *
     */
    @Override
    public void init(KeveriClientConfigure cfg) {
        // 1. 解析配置
        Properties prop = cfg.getProps();
        this.env = cfg.getEnv();

        // 2. 装填配置
        Properties nacosProp = new Properties();
        nacosProp.put(SERVER_ADDR, cfg.getServerLists());
        nacosProp.put(NAMESPACE, prop.getProperty(NAMESPACE, ""));
        nacosProp.put(USERNAME, prop.getProperty(USERNAME, ""));
        nacosProp.put(PASSWORD, prop.getProperty(PASSWORD, ""));
        nacosProp.put(ACCESS_KEY, prop.getProperty(ACCESS_KEY, ""));
        nacosProp.put(SECRET_KEY, prop.getProperty(SECRET_KEY, ""));

        // 3. 创建服务
        try {
            this.namingService = NamingFactory.createNamingService(nacosProp);
            this.namingMaintainService = NamingMaintainFactory.createMaintainService(nacosProp);
        } catch (NacosException e) {
            throw new KeveriException(NACOS_INIT_ERROR);
        }
    }

    /**
     * 服务注册
     */
    @Override
    public void register(ServiceDefinition serviceDefinition, ServiceInstance serviceInstance) {
        try {
            // 1. 构造 Nacos 实例信息
            Instance instance = new Instance();
            instance.setInstanceId(serviceInstance.getServiceInstanceId());
            instance.setPort(serviceInstance.getPort());
            instance.setIp(serviceInstance.getIp());
            instance.setMetadata(Map.of(META_DATA_KEY, JSON.toJSONString(serviceInstance)));

            // 2. 初始化服务
            namingService.registerInstance(serviceDefinition.getServiceId(), env, instance);
            namingMaintainService.updateService(serviceDefinition.getServiceId(), env, 0, Map.of(META_DATA_KEY, JSON.toJSONString(serviceDefinition)));

            log.info("[keveri-cloud] 注册服务成功：Definition is {}, Instants {}", serviceDefinition, serviceInstance);
        } catch (NacosException ex) {
            throw new KeveriException(NACOS_REGISTRY_ERROR);
        }
    }

    /**
     * 服务注销
     */
    @Override
    public void deregister(ServiceDefinition serviceDefinition, ServiceInstance serviceInstance) {
        try {
            //进行服务注销
            namingService.deregisterInstance(serviceDefinition.getServiceId(), env, serviceInstance.getIp(),
                    serviceInstance.getPort());
        } catch (NacosException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     *
     */
    @Override
    public void closeRepository() {
        try {
            namingService.shutDown();
            namingMaintainService.shutDown();
        } catch (NacosException e) {
            log.error("[Keveri-Cloud] Nacos Shutdown 时出现异常 ");
        }
    }

    /**
     *
     */
    private final List<GatewayServiceListener> listeners = new CopyOnWriteArrayList<>();

    /**
     *
     */
    @Override
    public void subscribeTotalGatewayServices(GatewayServiceListener listener) {
        // 1. 保存监听器
        listeners.add(listener);

        // 2.
        doSubscribeTotalGatewayServices();

        // 3. 初始化线程池
        ScheduledExecutorService stp = Executors.newScheduledThreadPool(1, new NameThreadFactory(
                "SubscribeGatewayServiceTP"));

        // 4. 定时触发
        stp.scheduleWithFixedDelay(this::doSubscribeTotalGatewayServices, 10, 10, TimeUnit.SECONDS);
    }

    private void doSubscribeTotalGatewayServices() {
        try {

            // 1. 提取
            Set<String> subscribeServiceSet = namingService.getSubscribeServices()
                    .stream()
                    .map(ServiceInfo::getName)
                    .collect(Collectors.toSet());

            // 2. 处理
            int pageNo = 1;
            int pageSize = 100;
            List<String> serviseList = namingService.getServicesOfServer(pageNo, pageSize, env).getData();
            while (CollectionUtils.isNotEmpty(serviseList)) {
                for (String service : serviseList) {
                    if (subscribeServiceSet.contains(service)) {
                        continue;
                    }
                    EventListener eventListener = new NacosGatewayServiceListener();
                    eventListener.onEvent(new NamingEvent(service, null));
                    namingService.subscribe(service, env, eventListener);
                    log.info("[Keveri-Cloud] 订阅了一个服务 ，ServiceName: {} 环境为: {}", service, env);
                }
                serviseList = namingService.getServicesOfServer(++pageNo, pageSize, env).getData();
            }
        } catch (NacosException e) {
            throw new KeveriException(e);
        }
    }

    /**
     * 内部监听器实现
     */
    private final class NacosGatewayServiceListener implements EventListener {
        @Override
        public void onEvent(Event event) {
            if (event instanceof NamingEvent namingEvent) {
                log.info("[Keveri-Cloud] 收到注册中心服务变更事件， event: {}", JSON.toJSONString(event));

                String serviceName = namingEvent.getServiceName();
                try {
                    // 1. 获取服务定义信息
                    Service service = namingMaintainService.queryService(serviceName, env);

                    // 2. 解析 MetaData
                    String jsonMeta = service.getMetadata().get(META_DATA_KEY);
                    ServiceDefinition definition = JSON.parseObject(jsonMeta, ServiceDefinition.class);

                    // 2. 提取实例数据
                    List<Instance> instances = namingService.getAllInstances(service.getName(), env);
                    Set<ServiceInstance> instanceSet = new HashSet<>();
                    for (Instance instance : instances) {
                        ServiceInstance serviceInstance = JSON
                                .parseObject(instance.getMetadata().get(META_DATA_KEY), ServiceInstance.class);
                        instanceSet.add(serviceInstance);
                    }

                    // 3. 循环监听器
                    listeners.forEach(listener -> listener.onChange(definition, instanceSet));
                } catch (NacosException e) {
                    throw new KeveriException(e);
                }
            }
        }
    }
}
